package com.lambkit.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import com.lambkit.core.Lambkit;
import com.lambkit.core.service.ResourceService;
import com.lambkit.core.service.ScanFileProcess;
import com.lambkit.util.StringKit;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author yangyong(孤竹行)
 */
public class ResourceServiceImpl implements ResourceService {

    private final String CLASSPATH_URL_PREFIX = "classpath:";

    private String webRootPath;

    @Override
    public String getRootClassPath(String resourcePathType) {
        return ClassUtil.getClassPath();
    }

    @Override
    public String getRootClassPath() {
        return getRootClassPath(Lambkit.context().getResourcePathType());
    }


    @Override
    public String getWebResourceClassPath(String resourcePath) {
        String prefix = resourcePath;
        if(prefix.startsWith("file:")) {
            prefix = prefix.substring(prefix.lastIndexOf("/"));
        }
        prefix = StringKit.removePrefix(prefix, "/");
        if (!prefix.endsWith("/")) {
            prefix = prefix + "/";
        }
        String path = CLASSPATH_URL_PREFIX + prefix;
        return path;
    }

    @Override
    public String getWebResourceWebRootPath(String resourcePath, String resourcePathType) {
        String webrootPath = null;
        String pathType = resourcePathType;
        if("classpath".equalsIgnoreCase(pathType) || "resources".equalsIgnoreCase(pathType)) {
            //共享资源放在jar同目录的webapp下
            String prefix = resourcePath;
            if(prefix.startsWith("file:")) {
                prefix = prefix.substring(prefix.lastIndexOf("/"));
            }
            prefix = StringKit.removePrefix(prefix, "/");
            if (!prefix.endsWith("/")) {
                prefix = prefix + "/";
            }
            webrootPath = getRootPath() + File.separator + prefix;
        } else {
            webrootPath = getWebRootPath(resourcePathType);
        }
        return webrootPath;
    }

    @Override
    public String getWebResourceFilePath(String resourcePath) {
        String path = resourcePath;
        if(path.startsWith("file:")) {
            //资源在外部指定的目录
            return path;
        }
        String webFilePath = null;
        String prefix = resourcePath;
        prefix = StringKit.removeSuffix(prefix, "/");
        if (!prefix.endsWith("/")) {
            prefix = prefix + "/";
        }
        webFilePath = getRootPath() + File.separator + prefix;
        return webFilePath;
    }

    @Override
    public List<String> getWebResourcePathList(String resourcePath, String resourcePathType) {
        List<String> pathList = CollUtil.newArrayList();

        String path = resourcePath;
        if(path.startsWith("file:")) {
            //资源在外部指定的目录
            pathList.add(path);
        }
        if(path.startsWith("file:")) {
            path = path.substring(5);
        }
        path = StringKit.removePrefix(path, "/");
        if (!path.endsWith("/")) {
            path = path + "/";
        }
        String resourceClassPath = CLASSPATH_URL_PREFIX + path;
        pathList.add(resourceClassPath);

        String resourceWebRootPath = getRootPath() + File.separator + path;
        pathList.add(resourceWebRootPath);

        String webrootPath = getWebRootPath(resourcePathType);
        pathList.add(webrootPath);

        String resourceFilePath = null;
        pathList.add(resourceFilePath);

        return pathList;
    }

    @Override
    public String getWebResourcePath(String resourcePath, String resourcePathType) {
        String pathType = resourcePathType;
        if("classpath".equalsIgnoreCase(pathType) || "resources".equalsIgnoreCase(pathType)) {
            String prefix = resourcePath;
            prefix = StringKit.removePrefix(prefix, "/");
            if(!prefix.endsWith("/")) {
                prefix = prefix + "/";
            }
            return CLASSPATH_URL_PREFIX + prefix;
        } else {
            String prefix = resourcePath;
            if(!prefix.startsWith("/")) {
                prefix = "/" + prefix;
            }
            prefix = StringKit.removeSuffix(prefix, "/");
            return getWebRootPath(resourcePathType) + prefix;
        }
    }

    @Override
    public String getWebTemplatePath(String templatePath, String resourcePathType) {
        if("classpath".equalsIgnoreCase(resourcePathType) || "resources".equalsIgnoreCase(resourcePathType)) {
            String prefix = templatePath;
            prefix = StringKit.removePrefix(prefix, "/");
            if(!prefix.endsWith("/")) {
                prefix = prefix + "/";
            }
            return CLASSPATH_URL_PREFIX + prefix;
        } else {
            String prefix = templatePath;
            if(!prefix.startsWith("/")) {
                prefix = "/" + prefix;
            }
            prefix = StringKit.removeSuffix(prefix, "/");
            return getWebRootPath(resourcePathType) + prefix;
        }
    }

    @Override
    public String getWebSharePath(String resourcePath, String resourcePathType) {
        if(resourcePath.startsWith("file:")) {
            //资源在外部指定的目录
            return resourcePath;
        } else {
            if("classpath".equalsIgnoreCase(resourcePathType) || "resources".equalsIgnoreCase(resourcePathType)) {
                return getRootPath() + File.separator + "webapp";
            } else {
                return getWebRootPath(resourcePathType);
            }
        }
    }

    @Override
    public String getWebRootPath(String resourcePathType) {
        if (webRootPath == null) {
            if("classpath".equalsIgnoreCase(resourcePathType) || "resources".equalsIgnoreCase(resourcePathType)) {
                webRootPath = CLASSPATH_URL_PREFIX;
            } else {
                webRootPath = detectWebRootPath();
            }

        }
        return webRootPath;
    }

    @Override
    public String getWebRootPath() {
        return getWebRootPath(Lambkit.context().getResourcePathType());
    }

    public void setWebRootPath(String webRootPath) {
        if (webRootPath != null) {
            if (webRootPath.endsWith(File.separator)) {
                webRootPath = webRootPath.substring(0, webRootPath.length() - 1);
            }
            this.webRootPath = webRootPath;
        }
    }

    private String detectWebRootPath() {
        try {
            String path = Lambkit.class.getResource("/").toURI().getPath();
            String ret = (new File(path)).getParentFile().getParentFile().getCanonicalPath();
            if (path.endsWith("/target/classes/")) {
                return ret + "/src/main/webapp";
            } else {
                return path.endsWith("\\target\\classes\\") ? ret + "\\src\\main\\webapp" : ret;
            }
        } catch (Exception var2) {
            throw new RuntimeException(var2);
        }
    }

    @Override
    public String getRootPath() {
        return System.getProperty("user.dir");
    }

    @Override
    public String getResourceFile(String fileName) {
        String filePath = null;
        String rootPath = getRootPath();
        //优先查找config下有没有properties
        String fileNamePath = rootPath + File.separator + "config" + File.separator + fileName;
        File file = new File(fileNamePath);
        if(file.exists()) {
            filePath = fileNamePath;
        } else {
            //优先查找root下有没有properties
            fileNamePath = rootPath + File.separator + fileName;
            if(file.exists()) {
                filePath = fileNamePath;
            } else {
                filePath = fileName;
            }
        }
        return filePath;
    }

    @Override
    public void scanFolder(String folderPath, String resourcePathType, ScanFileProcess fileProcess) {
        boolean isFile = true;
        //Printer.print(this, "resource"(getClass() + " scanFolder folderPath: " + folderPath);
        if("classpath".equalsIgnoreCase(resourcePathType)) {
            String path = folderPath.substring(CLASSPATH_URL_PREFIX.length());
            URL url = getClassLoader().getResource(path);
            isFile = false;
            //Printer.print(this, "resource"("url: " + url.toString());
            if(url != null) {
                try {
                    URLConnection urlConnection = url.openConnection();
                    if(urlConnection instanceof JarURLConnection) {
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        JarFile jarFile = jarURLConnection.getJarFile();
                        //Printer.print(this, "resource"("Jar Entry: " + jarURLConnection.getJarEntry().getName());
                        for (Enumeration<JarEntry> enumeration = jarFile.entries(); enumeration.hasMoreElements(); ) {
                            JarEntry jarEntry = enumeration.nextElement();
                            fileProcess.process(jarFile, jarEntry);
                        }
                    } else {
                        isFile = true;
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                isFile = true;
            }
        }
        if(isFile) {
            File file = new File(folderPath);
            scanTemplateFloders(file, fileProcess);
        }
    }

    @Override
    public void scanFolder(String folderPath, ScanFileProcess fileProcess) {
        scanFolder(folderPath, Lambkit.context().getResourcePathType(), fileProcess);
    }

    private void scanTemplateFloders(File file, ScanFileProcess fileProcess) {
        boolean flag = fileProcess.process(file);
        if(flag) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (null != files) {
                for (File f : files) {
                    scanTemplateFloders(f, fileProcess);
                }
            }
        }
    }

    /**
     * 优先使用 current thread 所使用的 ClassLoader 去获取路径
     * 否则在某些情况下会获取到 tomcat 的 ClassLoader，那么路径值将是
     * TOMCAT_HOME/lib
     *
     * issue: https://gitee.com/jfinal/jfinal/issues/ID428#note_699360
     */
    private ClassLoader getClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        }
        catch (Throwable ex) {
            // Cannot access thread context ClassLoader - falling back...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = Lambkit.class.getClassLoader();
            if (cl == null) {
                // getClassLoader() returning null indicates the bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                }
                catch (Throwable ex) {
                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
                }
            }
        }
        return cl;
    }
}
